package com.baidu.elinkagescroll;

import com.baidu.elinkagescroll.utils.LogUtil;

import com.ryan.ohos.extension.EstimateHelper;
import com.ryan.ohos.extension.ViewConfiguration;
import com.ryan.ohos.extension.event.DispatchHelper;
import com.ryan.ohos.extension.event.impl.ViewGroupHelper;
import com.ryan.ohos.extension.event.interfaces.ViewGroup;
import com.ryan.ohos.extension.nested.NestedScrollingParent;
import com.ryan.ohos.extension.nested.NestedScrollingParentHelper;
import com.ryan.ohos.extension.scroller.Scroller;

import ohos.agp.components.*;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.multimodalinput.event.TouchEvent;

import java.util.HashMap;
import java.util.Map;

/**
 * description :
 *
 * @since 2021/7/12
 */
public class ELinkageScrollLayout extends DirectionalLayout implements Component.TouchEventListener,
        ViewGroup, NestedScrollingParent, Component.EstimateSizeListener, ComponentContainer.ArrangeListener,
        Component.DrawTask {
    public static final boolean DEBUG = false;

    /**
     * Fling方向：以手指滑动方向为准
     */
    public static final int FLING_ORIENTATION_UP = 0x11;
    /**
     * Fling方向：以手指滑动方向为准
     */
    public static final int FLING_ORIENTATION_DOWN = 0x12;
    /**
     * Fling方向：以手指滑动方向为准
     */
    public static final int FLING_ORIENTATION_NONE = 0x00;
    private final MyHandler mHandler = new MyHandler(EventRunner.getMainEventRunner());
    /**
     * NestedScrollingParentHelper
     */
    private NestedScrollingParentHelper mParentHelper;
    /**
     * 所有子view的边界位置
     */
    private HashMap<Component, ViewEdge> mEdgeList = new HashMap<>();
    /**
     * 联动容器可滚动的范围
     */
    private int mScrollRange;
    /**
     * 跟踪速度的scroller，仅用于计算速度
     */
    private Scroller mVelocityScroller;
    /**
     * 联动容器滚动的Scroller
     */
    private Scroller mScroller;
    /**
     * VelocityTracker
     */
    private VelocityDetector mVelocityTracker;
    /**
     * 手势辅助工具类
     */
    private PosIndicator mPosIndicator;
    /**
     * MaximumVelocity
     */
    private int mMaximumVelocity;
    /**
     * MinimumVelocity
     */
    private int mMinimumVelocity;
    /**
     * 纪录fling方向
     */
    private int mFlingOrientation = FLING_ORIENTATION_NONE;
    /**
     * 是否拦截事件
     */
    private boolean mIsIntercept;
    /**
     * 纪录上一次的坐标
     */
    private int mLastY;
    private ViewGroupHelper impl;
    private ScrollApiHelper mScrollApiHelper;
    private SmoothScrollRunnable mScrollerRunnable;
    private int currentY;
    private Paint mPaint;
    private boolean isScorllBarShown;
    /**
     * child view的滚动事件
     */
    private final ChildLinkageEvent mChildLinkageEvent = new ChildLinkageEvent() {
        @Override
        public void onContentScrollToTop(Component target) {
            if (DEBUG) {
                LogUtil.loge("#onContentScrollToTop#");
            }
            // 如果收到子view的ContentScrollToTop事件，mFlingOrientation必须是FLING_ORIENTATION_DOWN
            if (mFlingOrientation != FLING_ORIENTATION_DOWN) {
                if (DEBUG) {
                    LogUtil.loge("onContentScrollToTop, Invalid Fling Orientation");
                }
                return;
            }
            // 安全性判断
            if (isFirstTarget(target)) {
                return;
            }
            if (!getLinkageScrollHandler(target).isScrollable()) {
                return;
            }
            ViewEdge targetEdge = getTargetEdge(target);
            if (targetEdge == null) {
                return;
            }
            if (getScrollValue(Component.AXIS_Y) != targetEdge.topEdge) {
                return;
            }
            if (!mVelocityScroller.computeScrollOffset()) {
                float currVelocity = mVelocityScroller.getCurrVelocity();
                currVelocity = currVelocity < 0 ? currVelocity : -currVelocity;
                if (DEBUG) {
                    LogUtil.loge("onContentScrollToTop, currVelocity: " + currVelocity);
                }
                mVelocityScroller.abortAnimation();
                parentFling(currVelocity);
            }
        }

        @Override
        public void onContentScrollToBottom(Component target) {
            if (DEBUG) {
                LogUtil.loge("#onContentScrollToBottom#");
            }
            // 如果收到子view的ContentScrollToBottom事件，mFlingOrientation必须是FLING_ORIENTATION_UP
            if (mFlingOrientation != FLING_ORIENTATION_UP) {
                if (DEBUG) {
                    LogUtil.loge("onContentScrollToBottom, Invalid Fling Orientation");
                }
                return;
            }
            if (isLastTarget(target)) {
                return;
            }
            if (!getLinkageScrollHandler(target).isScrollable()) {
                return;
            }
            ViewEdge targetEdge = getTargetEdge(target);
            if (targetEdge == null) {
                return;
            }
            if (getScrollValue(Component.AXIS_Y) != targetEdge.topEdge) {
                return;
            }
            if (!mVelocityScroller.computeScrollOffset()) {
                float currVelocity = mVelocityScroller.getCurrVelocity();
                currVelocity = currVelocity > 0 ? currVelocity : -currVelocity;
                if (DEBUG) {
                    LogUtil.loge("onContentScrollToBottom, currVelocity: " + currVelocity);
                }
                mVelocityScroller.abortAnimation();
                parentFling(currVelocity);
            }
        }

        @Override
        public void onContentScroll(Component target) {
        }
    };

    /**
     * init ELinkageScrollLayout
     *
     * @param context
     */
    public ELinkageScrollLayout(Context context) {
        this(context, null);
    }

    /**
     * init ELinkageScrollLayout
     *
     * @param context context
     * @param attrSet attrSet
     */
    public ELinkageScrollLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    /**
     * init ELinkageScrollLayout
     *
     * @param context context
     * @param attrSet attrSet
     * @param styleName styleName
     */
    public ELinkageScrollLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        mParentHelper = new NestedScrollingParentHelper(this);
        mScroller = new Scroller(context);
        mVelocityScroller = new Scroller(context);
        mPosIndicator = new PosIndicator(true);
        mMaximumVelocity = ViewConfiguration.getMaximumFlingVelocity();
        mMinimumVelocity = ViewConfiguration.getMinimumFlingVelocity();
        mPosIndicator.setTouchSlop(ViewConfiguration.getTouchSlop());
        mScrollerRunnable = new SmoothScrollRunnable(this);
        mPaint = new Paint();
        mPaint.setColor(new Color(Color.getIntColor("#00000000")));
        impl = new ViewGroupHelper(this);
        mScrollApiHelper = new ScrollApiHelper(this);
        mScrollApiHelper.setScrolledListener(new ScrollApiHelper.OnScrollApiHelperScrolledListener() {
            @Override
            public void onScrolled(int currentX, int currentY, int oldX, int oldY) {
                ELinkageScrollLayout.this.currentY = -currentY;
                // 显示滚动条,过一段时间后隐藏滚动条
                mPaint.setColor(new Color(Color.getIntColor("#AAA4A4A4")));
                isScorllBarShown = true;
                invalidate();
            }
        });
        setEstimateSizeListener(this);
        setArrangeListener(this);
        setTouchEventListener(this);
        addDrawTask(this);
    }

    /**
     * 初始化VelocityTracker
     */
    private void initOrResetVelocityTracker() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityDetector.obtainInstance();
        } else {
            mVelocityTracker.clear();
        }
    }

    /**
     * 初始化VelocityTracker
     */
    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityDetector.obtainInstance();
        }
    }

    /**
     * 回收VelocityTracker
     */
    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.clear();
            mVelocityTracker = null;
        }
    }

    @Override
    public boolean dispatchTouchEvent(TouchEvent ev) {
        float positionX = ev.getPointerScreenPosition(ev.getIndex()).getX();
        float positionY = ev.getPointerScreenPosition(ev.getIndex()).getY();

        switch (ev.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                mPosIndicator.onDown(positionX, positionY);

                resetScroll();
                initOrResetVelocityTracker();
                mVelocityTracker.addEvent(ev);
                break;
            case TouchEvent.POINT_MOVE:
                mPosIndicator.onMove(positionX, positionY);
                initVelocityTrackerIfNotExists();
                mVelocityTracker.addEvent(ev);
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mPosIndicator.onRelease(positionX, positionY);
                break;
        }

        return impl.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(TouchEvent ev) {
        final int action = ev.getAction();
        switch (action) {
            case TouchEvent.POINT_MOVE:
                // 拦截落在不可滑动子View的MOVE事件
                float rawX = ev.getPointerScreenPosition(ev.getIndex()).getX();
                float rawY = ev.getPointerScreenPosition(ev.getIndex()).getY();
                Component target = getTouchTarget((int) rawX, (int) rawY);
                if (mPosIndicator.isDragging()
                        && target != null
                        && !getLinkageScrollHandler(target).isScrollable()) {
                    mIsIntercept = true;
                    final ComponentParent parent = getComponentParent();
                    if (parent != null) {
                        DispatchHelper.requestDisallowInterceptTouchEvent(this, false);
                    }
                }
                break;
            case TouchEvent.PRIMARY_POINT_DOWN:
                mIsIntercept = false;
                // 校验一遍所有子view，内容是否滚动正确
                checkTargetsScroll();
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mIsIntercept = false;
                break;
        }

        return mIsIntercept;
    }

    @Override
    public boolean onTouchEvent(TouchEvent event) {
        switch (event.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                mLastY = (int) event.getPointerScreenPosition(event.getIndex()).getY();
                if (!mScrollerRunnable.isFinished()) {
                    mScrollerRunnable.removeTask();
                }
                break;
            case TouchEvent.POINT_MOVE:
                if (mLastY == 0) {
                    mLastY = (int) event.getPointerScreenPosition(event.getIndex()).getY();
                    return true;
                }
                int positionY = (int) event.getPointerScreenPosition(event.getIndex()).getY();
                int dy = positionY - mLastY;
                mLastY = positionY;
                mScrollApiHelper.scrollBy(0, -dy);
                break;
            case TouchEvent.CANCEL:
            case TouchEvent.PRIMARY_POINT_UP:
                mLastY = 0;
                if (mVelocityTracker != null) {
                    mVelocityTracker.calculateCurrentVelocity(1000, 0, mMaximumVelocity);
                    int yVelocity = (int) mVelocityTracker.getVerticalVelocity();
                    recycleVelocityTracker();
                    parentFling(-yVelocity);
                }
                break;
        }
        return true;
    }

    /**
     * 重置滚动变量
     */
    private void resetScroll() {
        mFlingOrientation = FLING_ORIENTATION_NONE;
        mVelocityScroller.abortAnimation();
        mScroller.abortAnimation();
    }

    /**
     * 获取手指触摸的target
     *
     * @param rawX rawX
     * @param rawY rawY
     * @return 手指触摸的target
     */
    private Component getTouchTarget(float rawX, float rawY) {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            Component target = getComponentAt(i);
            float[] contentPosition = target.getContentPosition();
            int left = (int) contentPosition[0];
            int top = (int) contentPosition[1];
            int right = left + target.getWidth();
            int bottom = top + target.getHeight();
            RectFloat rect = new RectFloat(left, top, right, bottom);
            if (rect.isInclude(rawX, rawY)) {
                return target;
            }
        }
        return null;
    }

    /**
     * scroll parent中的子view
     *
     * @param positionX positionX
     * @param positionY positionY
     * @param isInertial 是否是惯性滚动
     */
    private void scrollTo(int positionX, int positionY, boolean isInertial) {
        // 区分惯性滚动和非惯性滚动
        // 1. 上边界和下边界检查时一致
        // 2. 惯性滚动增加中间态的edge检查
        if (isInertial) {
            positionY = positionY < 0 ? 0 : positionY;
            positionY = positionY > mScrollRange ? mScrollRange : positionY;

            // 获取下一个滚动边界
            int edge = getNextEdge();
            if (mFlingOrientation == FLING_ORIENTATION_UP) {
                positionY = positionY > edge ? edge : positionY;
            }
            if (mFlingOrientation == FLING_ORIENTATION_DOWN) {
                positionY = positionY < edge ? edge : positionY;
            }
            // 执行滚动
            scrollTo(positionX, positionY);

            // 如果fling到边界，子view继续fling
            int scrollY = getScrollValue(Component.AXIS_Y);
            if (scrollY == edge) {
                int velocity = (int) mScroller.getCurrVelocity();
                if (mFlingOrientation == FLING_ORIENTATION_UP) {
                    velocity = velocity > 0 ? velocity : -velocity;
                }
                if (mFlingOrientation == FLING_ORIENTATION_DOWN) {
                    velocity = velocity < 0 ? velocity : -velocity;
                }
                if (DEBUG) {
                    LogUtil.loge("#scrollTo# To Edge: " + edge + ", velocity: " + velocity);
                }
                mScroller.abortAnimation();
                Component target = getTargetByEdge(edge);
                getLinkageScrollHandler(target)
                        .flingContent(target, velocity);
                trackVelocity(velocity);
            }
        } else {
            positionY = positionY < 0 ? 0 : positionY;
            positionY = positionY > mScrollRange ? mScrollRange : positionY;
            scrollTo(positionX, positionY);
        }

    }

    @Override
    public void scrollBy(int scrollX, int scrollY) {
        mScrollApiHelper.scrollBy(scrollX, scrollY);
    }

    @Override
    public void scrollTo(int scrollX, int scrollY) {
        mScrollApiHelper.scrollTo(scrollX, scrollY);
    }

    /**
     * 根据边界值获取对应的target
     *
     * @param edge edge
     * @return Component
     */
    private Component getTargetByEdge(int edge) {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            Component target = getComponentAt(i);
            ViewEdge viewEdge = mEdgeList.get(target);
            if (viewEdge.topEdge == edge) {
                return target;
            }
        }
        return null;
    }

    /**
     * 根据fling的方向获取下一个滚动边界，
     * 内部会判断下一个子View是否isScrollable，
     * 如果为false，会顺延取下一个target的edge。
     *
     * @return 下一个滚动边界
     */
    private int getNextEdge() {
        int scrollY = getScrollValue(Component.AXIS_Y);
        if (mFlingOrientation == FLING_ORIENTATION_UP) {
            // 从上往下遍历
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                Component target = getComponentAt(i);
                LinkageScrollHandler targetLinkageHandler
                        = getLinkageScrollHandler(target);
                int topEdge = mEdgeList.get(target).topEdge;
                if (topEdge <= scrollY
                        || !isTargetScrollable(target)
                        || !targetLinkageHandler.canScrollVertically(DRAG_UP)) {
                    continue;
                }
                return topEdge;
            }
        } else if (mFlingOrientation == FLING_ORIENTATION_DOWN) {
            // 从下往上遍历
            for (int i = getChildCount() - 1; i >= 0; i--) {
                Component target = getComponentAt(i);
                LinkageScrollHandler targetLinkageHandler
                        = getLinkageScrollHandler(target);
                int topEdge = mEdgeList.get(target).topEdge;
                if (topEdge >= scrollY
                        || !isTargetScrollable(target)
                        || !targetLinkageHandler.canScrollVertically(DRAG_DOWN)) {
                    continue;
                }
                return topEdge;
            }
        } else {
            throw new SecurityException("#getNextEdge# unknown Fling Orientation");
        }
        return mFlingOrientation == FLING_ORIENTATION_UP ? mScrollRange : 0;
    }

    /**
     * parent fling
     * <p>
     * 1. 当parent需要直接消费fling，会调用parentFling
     * 2. 当子view惯性滚动到顶或者底，会调用parentFling
     *
     * @param velocityY velocityY
     */
    private void parentFling(float velocityY) {
        if (Math.abs(velocityY) > mMinimumVelocity) {
            mFlingOrientation = -velocityY > 0 ? FLING_ORIENTATION_UP : FLING_ORIENTATION_DOWN;
            mScrollerRunnable.start(
                    0, getScrollValue(Component.AXIS_Y),
                    1, (int) -velocityY,
                    Integer.MAX_VALUE, Integer.MAX_VALUE
            );
            invalidate();
        }
    }

    @Override
    public int getScrollValue(int direction) {
        if (direction == Component.AXIS_X) {
            return mScrollApiHelper.getScrollX();
        } else {
            return mScrollApiHelper.getScrollY();
        }
    }

    /**
     * 判断target是否是容器中第一个view
     *
     * @param target target
     * @return target是否是容器中第一个view
     */
    private boolean isFirstTarget(Component target) {
        Component first = getComponentAt(0);
        return target == first;
    }

    /**
     * 判断target是否是容器中最后一个view
     *
     * @param target target
     * @return target是否是容器中最后一个view
     */
    private boolean isLastTarget(Component target) {
        Component last = getComponentAt(getChildCount() - 1);
        return target == last;
    }

    /**
     * 校验子view内容滚动位置是否正确
     */
    private void checkTargetsScroll() {
        int scrollY = getScrollValue(Component.AXIS_Y);
        Component target = null;
        for (Map.Entry<Component, ViewEdge> entry : mEdgeList.entrySet()) {
            ViewEdge value = entry.getValue();
            if (scrollY >= value.topEdge && scrollY < value.bottomEdge) {
                target = entry.getKey();
                break;
            }
        }
        if (target == null) {
            return;
        }
        int index = getChildIndex(target);
        if (DEBUG) {
            LogUtil.loge("#checkTargetsScroll# index: " + index);
        }
        for (int i = 0; i < index; i++) {
            final Component child = getComponentAt(i);
            scrollTargetContentToBottom(child);
        }
        for (int i = index + 1; i < getChildCount(); i++) {
            final Component child = getComponentAt(i);
            scrollTargetContentToTop(child);
        }
    }

    /**
     * 跟踪加速度
     *
     * @param velocityY velocityY
     */
    private void trackVelocity(float velocityY) {
        mVelocityScroller.fling(0, 0,
                0, (int) velocityY,
                0, 0,
                Integer.MIN_VALUE, Integer.MAX_VALUE);
        invalidate();
    }

    /**
     * 获取target的LinkageScrollHandler接口
     *
     * @param target target
     * @return target的LinkageScrollHandler接口
     */
    private LinkageScrollHandler getLinkageScrollHandler(Component target) {
        return ((ILinkageScroll) target).provideScrollHandler();
    }

    /**
     * 判断target是否可滚动
     *
     * @param target target
     * @return target是否可滚动
     */
    private boolean isTargetScrollable(Component target) {
        LinkageScrollHandler linkageScrollHandler
                = getLinkageScrollHandler(target);
        boolean isScrollable = linkageScrollHandler.isScrollable();
        return isScrollable && (target.getHeight() >= getHeight());
    }

    @Override
    public boolean onEstimateSize(int widthSpecConfig, int heightSpecConfig) {
        int size = getChildCount();
        for (int i = 0; i < size; i++) {
            Component child = getComponentAt(i);
            if (child instanceof ListContainer) {
                ListContainer container = (ListContainer) child;
                Component listItem = container.getItemProvider().getComponent(0, null, container);
                measureChild(listItem, widthSpecConfig, heightSpecConfig);
                int itemHeight = listItem.getEstimatedHeight();
                int listHeight = itemHeight * container.getChildCount();
                final ComponentContainer.LayoutConfig lp = child.getLayoutConfig();
                final int childWidthMeasureSpec = EstimateHelper.getChildMeasureSpec(widthSpecConfig,
                        getPaddingLeft() + getPaddingRight(), lp.width);

                final int childHeightMeasureSpec = EstimateHelper.getChildMeasureSpec(heightSpecConfig,
                        getPaddingTop() + getPaddingBottom(), listHeight);
                container.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
            } else {
                measureChild(child, widthSpecConfig, heightSpecConfig);
            }
        }
        setEstimatedSize(EstimateSpec.getSize(widthSpecConfig), EstimateSpec.getSize(heightSpecConfig));
        return true;
    }

    protected void measureChild(Component child, int parentWidthMeasureSpec,
                                int parentHeightMeasureSpec) {
        final ComponentContainer.LayoutConfig lp = child.getLayoutConfig();
        final int childWidthMeasureSpec = EstimateHelper.getChildMeasureSpec(parentWidthMeasureSpec,
                getPaddingLeft() + getPaddingRight(), lp.width);
        final int childHeightMeasureSpec = EstimateHelper.getChildMeasureSpec(parentHeightMeasureSpec,
                getPaddingTop() + getPaddingBottom(), lp.height);
        child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    @Override
    public boolean onArrange(int left, int top, int width, int height) {
        mScrollRange = 0;
        int scrollY = mScrollApiHelper.getScrollY();
        int childTop = top - scrollY;
        int size = getChildCount();
        for (int i = 0; i < size; i++) {
            Component child = getComponentAt(i);
            int childBottom = childTop + child.getEstimatedHeight();
            child.arrange(left, childTop, child.getEstimatedWidth(), child.getEstimatedHeight());
            childTop = childBottom;
            // 联动容器可滚动最大距离
            mScrollRange += child.getEstimatedHeight();
            mEdgeList.put(child, new ViewEdge(child.getTop() + scrollY, child.getBottom() + scrollY));
        }
        // 联动容器可滚动range
        mScrollRange -= getEstimatedHeight();
        mScrollApiHelper.setOffsetToScrollY(scrollY);
        mScrollApiHelper.setScrollRangeY(mScrollRange);
        mScrollApiHelper.resetSavedPositions();
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        int height = component.getEstimatedHeight() * component.getEstimatedHeight() / mScrollRange;
        int top = (component.getEstimatedHeight() - height) * currentY / mScrollRange;
        LogUtil.loge("top = " + top + "; currentY = " + currentY);
        if (isScorllBarShown) {
            mHandler.removeAllEvent();
            mHandler.sendEvent(100, 1000);
        }
        canvas.drawRect(component.getRight() - 15, top, component.getRight(), top + height, mPaint);
    }

    @Override
    public boolean onStartNestedScroll(Component child, Component target, int axes, int type) {
        if (DEBUG) {
            LogUtil.loge("#onStartNestedScroll# nestedScrollAxes: " + axes);
        }
        return axes != 0;
    }

    /**
     * 当onStartNestedScroll返回true，该方法会被调用。
     * <p>
     * 可以在这个方法中执行一些初始化操作
     *
     * @param child child
     * @param target target
     * @param axes axes
     * @param type type
     */
    @Override
    public void onNestedScrollAccepted(Component child, Component target, int axes, int type) {
        if (DEBUG) {
            LogUtil.loge("#onNestedScrollAccepted# axes: " + axes);
        }
        mParentHelper.onNestedScrollAccepted(child, target, axes, type);

        // 初始化
        resetScroll();
    }

    @Override
    public void onStopNestedScroll(Component child, int type) {
        if (DEBUG) {
            LogUtil.loge("#onStopNestedScroll# child: " + child);
        }
        mParentHelper.onStopNestedScroll(child, type);
    }

    @Override
    public void onNestedScroll(Component target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed) {
        // target消费一部分，parent消费剩下的
        if (dyConsumed != 0 && dyUnconsumed != 0) {
            mScrollApiHelper.scrollBy(0, dyUnconsumed);
        }
    }

    @Override
    public void onNestedPreScroll(Component target, int dx, int dy, int[] consumed, int type) {
        if (DEBUG) {
            LogUtil.loge("#onNestedPreScroll# dy: " + dy);
        }
        // 1. parent处理scroll
        // 1.1 parent全部消费
        // 1.2 parent消费一部分, target消费剩余的
        // 2. target处理scroll
        // 2.1 target消费所有scroll
        // 2.2 target消费一部分，parent消费剩余的
        boolean moveUp = dy > 0;
        boolean moveDown = !moveUp;
        int scrollY = getScrollValue(Component.AXIS_Y);
        ViewEdge targetEdge = getTargetEdge(target);
        LinkageScrollHandler targetScrollHandler
                = ((ILinkageScroll) target).provideScrollHandler();

        if (scrollY == targetEdge.topEdge) {
            if ((moveDown && !targetScrollHandler.canScrollVertically(DRAG_DOWN))
                    || (moveUp && !targetScrollHandler.canScrollVertically(DRAG_UP))) {
                mScrollApiHelper.scrollBy(0, dy);
                consumed[1] = dy;
            } else {
                // target处理scroll
                if (DEBUG) {
                    LogUtil.loge("#onNestedPreScroll#, handle scroll by " + target);
                }
            }
        } else if (scrollY > targetEdge.topEdge) {
            if (moveUp) {
                mScrollApiHelper.scrollBy(0, dy);
                consumed[1] = dy;
            }
            if (moveDown) {
                int end = scrollY + dy;
                int deltaY;
                deltaY = end > targetEdge.topEdge ? dy : (targetEdge.topEdge - scrollY);
                mScrollApiHelper.scrollBy(0, dy);
                consumed[1] = deltaY;
            }
        } else {
            if (moveDown) {
                mScrollApiHelper.scrollBy(0, dy);
                consumed[1] = dy;
            }
            if (moveUp) {
                int end = scrollY + dy;
                int deltaY;
                deltaY = end < targetEdge.topEdge ? dy : (targetEdge.topEdge - scrollY);
                mScrollApiHelper.scrollBy(0, dy);
                consumed[1] = deltaY;
            }
        }
    }

    /**
     * 直接跳转到某个子view
     *
     * @param index index
     */
    public void gotoChild(int index) {
        // 1. scroll到指定子view
        smoothScrollToIndex(index);
        // 2. child[0] - child[index-1]的子view内容都必须滚动到底部
        for (int i = 0; i < index; i++) {
            final Component target = getComponentAt(i);
            scrollTargetContentToBottom(target);
        }
        // 3. child[index] - child[childCount - 1]的子view内容都滚动到顶部
        for (int i = index; i < getChildCount(); i++) {
            final Component target = getComponentAt(i);
            scrollTargetContentToTop(target);
        }
    }

    /**
     * 滚动到某个子view
     *
     * @param index index
     */
    private void scrollToIndex(int index) {
        index = index < 0 ? 0 : index;
        index = index > getChildCount() - 1 ? getChildCount() - 1 : index;
        Component target = getComponentAt(index);
        ViewEdge viewEdge = mEdgeList.get(target);
        // 滚动到某个子view
        scrollTo(0, viewEdge.topEdge, false);
        invalidate();
    }

    /**
     * 平滑滚动到某个子view
     *
     * @param index index
     */
    private void smoothScrollToIndex(int index) {
        index = index < 0 ? 0 : index;
        index = index > getChildCount() - 1 ? getChildCount() - 1 : index;
        Component target = getComponentAt(index);
        ViewEdge viewEdge = mEdgeList.get(target);
        LogUtil.loge("getScrollValue(AXIS_Y) = " + getScrollValue(AXIS_Y) + "; viewTop = " + viewEdge.topEdge);
        mScrollerRunnable.start(0, -getScrollValue(AXIS_Y), 0, -(viewEdge.topEdge - getScrollValue(Component.AXIS_Y)));
    }

    /**
     * 滚动指定子view的内容到顶部
     *
     * @param target target
     */
    private void scrollTargetContentToTop(Component target) {
        LinkageScrollHandler targetScrollHandler
                = ((ILinkageScroll) target).provideScrollHandler();
        if (targetScrollHandler.isScrollable()
                && targetScrollHandler.canScrollVertically(DRAG_DOWN)) {
            targetScrollHandler.scrollContentToTop();
        }
    }

    /**
     * 滚动指定子view的内容到底部
     *
     * @param target target
     */
    private void scrollTargetContentToBottom(Component target) {
        LinkageScrollHandler targetScrollHandler
                = ((ILinkageScroll) target).provideScrollHandler();
        if (targetScrollHandler.isScrollable()
                && targetScrollHandler.canScrollVertically(DRAG_UP)) {
            targetScrollHandler.scrollContentToBottom();
        }
    }

    /**
     * 返回targetview的edge
     *
     * @param target component
     * @return targetview的edge
     */
    private ViewEdge getTargetEdge(Component target) {
        ViewEdge viewEdge = mEdgeList.get(target);
        return viewEdge;
    }

    /**
     * onNestedFling
     *
     * @param target target
     * @param velocityX velocityX
     * @param velocityY velocityY
     * @param consumed consumed
     * @return boolean
     */
    @Override
    public boolean onNestedFling(Component target, float velocityX, float velocityY, boolean consumed) {
        return false;
    }

    @Override
    public boolean onNestedPreFling(Component target, float velocityX, float velocityY) {
        if (DEBUG) {
            LogUtil.loge("#onNestedPreFling# velocityY: " + velocityY);
        }
        int scrollY = getScrollValue(Component.AXIS_Y);
        ViewEdge targetEdge = getTargetEdge(target);
        mFlingOrientation = velocityY > 0 ? FLING_ORIENTATION_UP : FLING_ORIENTATION_DOWN;
        if (scrollY == targetEdge.topEdge) {
            // 跟踪velocity，当target滚动到顶或底，保证parent继续fling
            trackVelocity(velocityY);
            return false;
        } else {
            parentFling(velocityY);
            return true;
        }
    }

    /**
     * view的上边沿和下边沿
     */
    private static class ViewEdge {
        public int topEdge;
        public int bottomEdge;

        ViewEdge(int topEdge, int bottomEdge) {
            this.topEdge = topEdge;
            this.bottomEdge = bottomEdge;
        }
    }

    @Override
    public int getNestedScrollAxes() {
        return mParentHelper.getNestedScrollAxes();
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        impl.requestDisallowInterceptTouchEvent(disallowIntercept);
    }

    @Override
    public boolean isConsumed() {
        return impl.isConsumed();
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        return DispatchHelper.dispatch(component, touchEvent);
    }

    class MyHandler extends EventHandler {
        MyHandler(EventRunner runner) {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event.eventId == 100) {
                // 隐藏滚动条
                if (mPaint != null) {
                    mPaint.setColor(new Color(Color.getIntColor("#00000000")));
                    isScorllBarShown = false;
                    invalidate();
                }
            }
        }
    }
}
